package org.dding.mvc;

import java.io.File;
import java.io.IOException;
import java.util.List;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.dding.core.ContainerFactory;
import org.dding.core.PlatformMBeanServer;
import org.dding.core.exception.LifecycleException;
import org.dding.core.log.ILogger;
import org.dding.core.log.LoggerFactory;
import org.dding.core.manager.IModuleManager;
import org.dding.core.manager.IServiceManager;
import org.dding.core.util.StandaloneXmlParser;
import org.dding.core.util.StandaloneXmlParser.Extension;
import org.dding.msc.exception.ServiceNotFoundException;
import org.dding.msc.exception.ServiceUnavailableException;
import org.dding.msc.service.Service;
import org.dding.msc.service.ServiceController;
import org.dding.msc.service.ServiceController.State;
import org.dding.msc.service.ServiceInfoWarehouse;
import org.dding.msc.service.ServiceInfoWarehouse.ServiceInfo;
import org.jboss.modules.ModuleLoadException;

public class WebContextLoader implements ServletContextListener {

	public static final String MODULES_DIR = "MODULES_DIR";
	public static String _modulesDir = null;

	public static final String configFile = "standalone.xml";

	private File modulesDir = null;

	private static IModuleManager container = null;

	private static ILogger logger = LoggerFactory
			.getLogger(WebContextLoader.class);

	public void contextInitialized(ServletContextEvent servletContextEvent) {

		long now = System.currentTimeMillis();
		try {
			calculationModuleDir(servletContextEvent);
			container = ContainerFactory.createModuleManager(modulesDir
					.getAbsolutePath());
			PlatformMBeanServer.initMBean(container);
			readConfig(servletContextEvent);
			container.start();
			loadExtensions();
			logger.info("server started in "
					+ ((System.currentTimeMillis() - now)) + " milliseconds!");
		} catch (Throwable e) {
			logger.error(e, "Start core failed!");
			System.exit(-1);
		}
	}

	private void calculationModuleDir(ServletContextEvent servletContextEvent) {
		_modulesDir = servletContextEvent.getServletContext().getInitParameter(
				MODULES_DIR);
		if (_modulesDir == null || _modulesDir.trim().length() == 0) {
			logger.warn("MODULES_DIR not configured in web.xml!");
		} else {
			if (!_modulesDir.startsWith("/")) {
				// forward url必须以 / 开头，否则 ControllerServlet forward 出错
				_modulesDir = "/" + _modulesDir;
				modulesDir = new File(servletContextEvent.getServletContext()
						.getRealPath("/"), _modulesDir);
			} else {
				// 支持绝对路径
				modulesDir = new File(_modulesDir);
			}

			if (!modulesDir.exists()) {
				logger.warn("Modules dir configured in web.xml not exists, "
						+ modulesDir.toString());
				return;
			}
			System.setProperty("dding.module.root",
					modulesDir.getAbsolutePath());
		}
	}

	public static IModuleManager getContainer() {
		return container;
	}

	public void contextDestroyed(ServletContextEvent servletContextEvent) {
		if (container != null)
			try {
				container.stop();
			} catch (LifecycleException e) {
				logger.info("stop module container failure.");
			}
	}

	private static Action getAction(String serviceName, String actionName) throws Exception {
		if (!isServiceExist(serviceName)) {
			throw new ServiceNotFoundException(serviceName);
		}
		Service<?> service;
		ServiceController<?> controller = findSerivice(serviceName);
		if (controller != null) {
			service = controller.getService();
		} else {
			throw new ActionNotFoundException("Can not found Action: "
					+ actionName + " in Module: ");// + moduleDirName);
		}
		return (Action) service;
	}

	public static void invokeAction(String serviceName, String actionName, InvocationContext invocationContext) throws Exception {
		Action action = WebContextLoader.getAction(serviceName, actionName);
		if (action == null) {
			ServiceInfo serviceInfo = ServiceInfoWarehouse.getInstance().get(
					serviceName);
			String message = "Can not found Action: " + actionName
					+ serviceInfo != null ? " in Module: "
					+ serviceInfo.moduleName : "";
			throw new ActionNotFoundException(message);
		}
		action.execute(invocationContext);
	}

	public static boolean isServiceExist(String serviceName) {
		ServiceController<?> controller = findSerivice(serviceName);
		if (controller == null)
			return false;
		State currentState = controller.getState();
		if (currentState == State.WAITING) {
			throw new ServiceUnavailableException("service " + serviceName
					+ "'s state is waiting.");
		}

		return true;
	}

	public static String getServicePath(String serviceName) {
		Service<?> service = getService(serviceName);
		if (service != null) {
			String jarPath = service.getClass().getProtectionDomain()
					.getCodeSource().getLocation().getFile();
			String path = jarPath.substring(0, jarPath.lastIndexOf("."));
			return path.substring(path.indexOf(_modulesDir));
		} else {
			logger.warn("can't find any service");
		}
		return null;
	}

	public static Service<?> getService(String serviceName) {
		ServiceController<?> controller = findSerivice(serviceName);
		Service<?> service = null;
		if (controller != null) {
			service = controller.getService();
		}
		return service;
	}

	private void readConfig(ServletContextEvent servletContextEvent) throws Throwable {
		String config = servletContextEvent.getServletContext()
				.getRealPath("/")
				+ "WEB-INF"
				+ File.separator
				+ "conf"
				+ File.separator + configFile;
		StandaloneXmlParser.parser(config);
	}

	private void loadExtensions() throws Throwable, ModuleLoadException {
		List<Extension> extensions = StandaloneXmlParser.getExtensionlist();
		if (extensions == null)
			return;
		for (Extension element : extensions) {
			runModuleLoad(element.moduleName, element.unzip);
		}
	}

	private void runModuleLoad(String moduleIdentifer, boolean unzip) throws IOException, ModuleLoadException {
		container.loadModule(moduleIdentifer, unzip);
	}

	private static ServiceController<?> findSerivice(String serviceName) {
		IServiceManager[] managers = container.getServiceManagers();
		if (managers == null) {
			logger.info("can't find any service");
			return null;
		}
		for (IServiceManager temp : managers) {
			ServiceController<?> controller = temp.getService(serviceName);
			if (controller != null)
				return controller;
		}
		return null;
	}
}